#pragma once
#include <iostream>
#include <assert.h>
#include <cstring>

namespace ys
{
	template<class iterator>
	class ReverseIterator
	{
	public:
		ReverseIterator(iterator it)
			:_it(it) 
		{}
		iterator operator++()
		{
			return _it--;
		}
		iterator operator++(int)
		{
			return _it--;
		}
		char operator*()
		{
			return *_it;
		}
		bool operator!=(const ReverseIterator<iterator>& str)
		{
			//当调用rend()时，会构造一个临时的常属性对象返回
			// 接着调用了operator!=()函数，传递的参数是，rend()返回的临时常量，所以要加const
			return _it != str._it;
		}
	private:
		iterator _it;
	};

	class string
	{
		//--------------friend reputation------------------//
		friend std::ostream& operator<<(std::ostream& out, const string& str);
	public:
		typedef char value_type;
		typedef char* iterator;
		typedef const char* const_iterator;
		typedef ReverseIterator<iterator> reverse_iterator;
		typedef ReverseIterator<const_iterator> const_reverse_iterator;
		//-------------Member functions-------------
		string()
			:_str(new char[1] {'\0'})
			, _size(0)
			, _capacity(0)
		{}
		string(const char* str)
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			//strlen计算的时候不会带\0，实际上str有_size个字符 + 1个\0字符
			//而我们开的空间也是_size + 1个，strcpy还可以帮我们拷贝\0
			strcpy(_str, str);
		}
		string(const string& str)
			:_size(str._size)
			,_capacity(str._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str.c_str());
		}
		//这里也要写成深拷贝，否则string str1 = str2; 当str1 和 str2都出作用域时也会析构两次
		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}
		~string()
		{
			delete _str;
			_str = nullptr;
		}
		//-----------------Iterator----------------
		iterator begin() { return _str; }
		iterator end() { return _str + _size; }

		const_iterator cbegin() const { return _str; }
		const_iterator end() const { return _str + _size; }

		reverse_iterator rbegin() { return reverse_iterator(_str + _size - 1); }
		reverse_iterator rend() { return reverse_iterator(_str - 1); }

		const_reverse_iterator crbegin() const { return const_reverse_iterator(_str + _size - 1); }
		const_reverse_iterator ernd() const { return const_reverse_iterator(_str - 1); }
		//-----------------Capacity----------------
		void reserve(size_t n)
		{
			//检查n是否大于当前容量，如果不大于则什么也不做
			if (n < _capacity) return;
			//如果大于的话，则重新申请空间
			value_type* tmp = new value_type[n + 1];
			//将原空间的数据拷贝过去(strcpy拷贝到\0字符会停止，但是拷贝包括\0字符)
			strcpy(tmp, _str);
			delete _str;
			_str = tmp;

			_capacity = n;
		}

		size_t size() const
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}

		void clear()
		{
			_size = 0;
			_str[_size] = 0;
		}
		
		//-------------Element access----------------
		//operator[]的const和非const版本不能合并是因为,可能会涉及str[n] = 'x'的操作
		//如果是const类型的对象，那么他是不允许修改字符串内容的，所以调用operator[]返回的是const char，进行上述的操作会报错
		//如果是非const类型的对象时，是允许修改字符串的，所以调用后会返回char 是可以修改的
		//因为不同版本对象调用的operator[]函数返回值不同所以不可以修改。
		//而size()函数，返回的内容没有修改的意义，所以不用引用返回，返回的都是具有常属性的临时变量，都不能修改
		//所以返回值一样，可以把不同版本对象合并在一起
		char& operator[](size_t i)
		{
			return _str[i];
		}
		const char& operator[](size_t i) const
		{
			return _str[i];
		}
		//--------------Modifiers------------------
		void push_back(const char c)
		{
			//判读是否需要扩容
			if (_size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}
			//进行插入操作
			_str[_size++] = c;
		}

		void pop_back()
		{
			assert(!empty());
			_size--;
		}

		string& insert(size_t pos, const string& str)
		{
			assert(pos >= 0);
			assert(pos <= _size);
			//判读是否需要扩容
			size_t n = str.size();
			if (_size + n > _capacity)
			{
				size_t newCapacity = std::max(n, (_capacity == 0 ? 4 : _capacity * 2));
				reserve(newCapacity);
			}
			//移动元素，在pos位置让出str.size()个空间出来
			int tail = _size - 1;
			while (tail >= (int)pos)
			{
				_str[tail + n] = _str[tail];
				tail--;
			}
			//放入
			for (int i = 0; i < n; i++)
			{
				_str[pos + i] = str[i];
				_size++;
			}
			_str[_size] = '\0';
			return *this;
		}

		string& erase(size_t pos, size_t len = npos)
		{
			assert(!empty());
			assert(pos >= 0);
			assert(pos <= _size);

			if (len == npos || pos + len >= _size)
			{
				_size = pos;
				_str[_size] = '\0';
				return *this;
			}

			int tail = pos + len;
			while (tail < _size)
			{
				_str[tail - len] = _str[tail];
				tail++;
			}
			_size -= len;
			_str[_size] = '\0';
			return *this;
		}

		string& operator+=(const char* str)
		{
			insert(_size, str);
			return *this;
		}
		//--------------------String operations---------------//

		char* c_str() const
		{
			return _str;
		}

		size_t find(const string& str, size_t pos = 0) const
		{
			assert(str.c_str());
			assert(pos < _size);

			char* tmp = strstr(_str + pos, str.c_str());
			return tmp - _str;
		}

		string substr(size_t pos = 0, size_t len = npos) const
		{
			//当返回的是string类型时，他会调用拷贝构造函数构造一个临时对象，默认的是浅拷贝
			//如果不一个深拷贝的话，tmp中的_str所指向的空间会析构两次
			//第一次tmp出作用域，第二次临时对象析构
			assert(pos < _size);
			if (len == npos || len + pos >= _size)
				return string(_str + pos);

			string tmp;
			for(size_t i = 0; i < len; i++)
			{
				tmp.push_back(_str[pos + i]);
			}
			tmp[len] = '\0';
			return tmp;
		}

		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

	public:
		static const size_t npos;
	private:
		value_type* _str;
		size_t _size;
		size_t _capacity;
	};
	const size_t string::npos = -1;

	//为什么要写在外面，因为我们常常是这样书写：std::cout << str; 这样的话，第一个参数传的是ostream类型的参数
	//而我们在类内，第一个传的是const* string 类型的，所以要卸载外面才能确保是上面的格式
	//至于返回值是ostream类型，是可以兼容连续 << 的操作
	std::ostream& operator<<(std::ostream& out, const string& str)
	{
		out << str._str << std::endl;
		return out;
	}

	std::istream& operator>>(std::istream& in, string& str)
	{
		//先清空字符串
		str.clear();
		//使用缓冲区的方式写入
		char buffer[128];
		int i = 0;
		
		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buffer[i++] = ch;
			if (i == 128)
			{
				buffer[i] = '\0';
				str += buffer;
				i = 0;
			}
			ch = in.get();
		}
		if (i != 0)
		{
			buffer[i] = '\0';
			str += buffer;
		}
		return in;
	}
}